let _vue = null;

export default class VueRouter {
  static install(vue) {
    // 1.判断插件是否已经安装
    if (VueRouter.install.installed) {
      return;
    }
    VueRouter.install.installed = true;
    // 2.把vue的构造函数记录的全局变量中
    _vue = vue;
    // 3.把创建vue实例时候传入的router对象注入的vue实例上
    // 混入，这里混入所有的vue实例都会有
    _vue.mixin({
      beforeCreate() {
        // 原型上挂在操作只需要执行一次，如果是组件不执行，如果是vue实例才执行
        if (this.$options.router) {
          console.log(this);
          _vue.prototype.$router = this.$options.router;
        }
      },
    });
  }

  constructor(options) {
    // 保存options
    this.options = options;
    this.data = _vue.observable({
      current: '/',
    });
    // 解析routes解析路由规则,键值对形式
    this.routeMap = {};
    this.init();
  }

  // 封装初始化的一些方法
  init() {
    // 调用保存地址和组件映射的方法
    this.createRouteMap();
    // 调用创建router-link和router-view组件的方法
    this.initComponents();
    // 调用解决浏览器前进后退不重新加载组件的方法
    this.initEvent();
  }

  // 把构造函数中选项内传递过来的routes转换成键值对的形式,存储到routeMap中
  createRouteMap() {
    // 遍历routes把path和component存到routerMap对象中;
    if (this.options.routes) {
      this.options.routes.forEach((route) => {
        this.routeMap[route.path] = route.component;
      });
    }
  }

  // 创建组件
  initComponents() {
    // 拿到当前模式
    // const { options: { mode } } = this;
    const self = this;
    const mode = self.options.mode;
    // 创建router-link组件, 两个参数,1组件名, 2组件参数
    _vue.component('router-link', {
      props: {
        to: String,
      },
      render(h) {
        // 第一个参数选择器，第二个参数设置的属性, 第三个参数设置子元素
        return h('a', {
          // 添加dom对象的属性写在attrs中
          attrs: {
            href: `${mode === 'history' ? '' : '#'}${this.to}`,
          },
          // 注册dom对象的事件
          on: {
            click: this.clickHandler,
          },
        }, [this.$slots.default]); // 获取子元素默认插槽
      },
      methods: {
        // 事件处理函数
        clickHandler(e) {
          // 1.调用pushState方法改变浏览器的地址,pushState接收三个参数
          // 1.触发pushState传给事件对象的参数,2. 网页标题 3. 地址
          if (this.$router.options.mode === 'history') {
            history.pushState({}, '', this.to);
          }else {
            window.location.hash = this.to
          }
          // 设置路由地址,之后才能根据这个地址加载对应的组件(这里的this是vue实例)
          // 因为这个属性是响应式的所以当这个属性值改变时会重新加载组件,(走下面的router-view组件)并时时渲染试图
          this.$router.data.current = this.to;
          e.preventDefault();
        },
      },
      // 运行时版本不支持template，只支持render函数
      // template: '<a :href="to"><slot></slot></a>'
    });
    // 创建router-view组件
    _vue.component('router-view', {
      // 利用h参数创建虚拟dom
      render(h) {
        // 获取当前路由地址(data.current)之后值是在router-link组件内设置的 找到对应的组件
        const component = self.routeMap[self.data.current];
        // h函数可以接收一个组件转换成一个虚拟dom
        return h(component);
      },
    });
  }

  // 解决浏览器前进后退不重新加载组件的方法
  initEvent() {
    if (this.options.mode === 'history') {
      window.addEventListener('popstate', () => {
        // 将响应式的保存路径的值设置成浏览器地址中路径部分的值即可
        this.data.current = window.location.pathname;
      });
    } else {
      window.addEventListener('hashchange', () => {
        this.data.current = window.location.hash.slice(1);
      });
      window.addEventListener('load', () => {
        this.data.current = window.location.hash.slice(1);
      });
    }
  }
}
